<template>
  <div class="custom-tree">
    <div v-for="node in flatTreeData" :key="node.id" :class="!node.isLastLayer ? 'tree-node' : 'tree-node-last'"
      :style="{ marginLeft: node.level * 20 + 'px' }">
      <div class="tree-node-label">
        <el-icon v-if="node.listChildMenu && node.listChildMenu.length > 0" @click="handleNodeClick(node)">
          <CaretRight :class="!node.expanded ? 'icon-caret-right' : 'icon-caret-bottom'" />
        </el-icon>
        <el-checkbox v-if="avaChecked" v-model="node.select" @change="handleCheckChange(node)" :disabled="node.disabled"
          :indeterminate="node.indeterminate"></el-checkbox>
        <span class="tree-node-label-text" @click="handleNodeClick(node)">{{ node.menuName }}</span>
      </div>

      <!-- 添加自定义内容 -->
      <div v-if="node.isLast" class="tree-node-custom-content">
        <slot name="treeOperation" :node="findMenuNode(flatTreeData, node.id)"></slot>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, watch, PropType } from 'vue';
import { ElCheckbox } from 'element-plus';
import { CaretRight } from '@element-plus/icons-vue';

interface TreeNode {
  id: number;
  menuName: string;
  expanded: boolean;
  select: boolean;
  disabled: boolean;
  indeterminate: boolean;
  level: number; // 新增属性，表示节点层级
  listChildMenu?: TreeNode[];
  parentId: number;
  isLast: boolean;
  isLastLayer: boolean;
  dataScope: any;
  deptIds: any;
}

const props = defineProps({
  treeData: {
    type: Array as PropType<TreeNode[]>,
    required: true,
  },
  avaChecked: {
    type: Boolean,
    default: false,
  },
  alleExpanded: {
    type: Boolean,
    default: false,
  },
  allChecked: {
    type: Boolean,
    default: false,
  },
  checkedKeys: {   // 选中的节点id
    type: Array as PropType<any[]>,
    default: () => []
  }
});

const flatTreeData = ref<TreeNode[]>([]);


const flattenTree = (nodes: TreeNode[], level: number = 0) => {
  nodes.forEach(node => {
    node.level = level;
    flatTreeData.value.push(node);
    if (node.expanded && node.listChildMenu && node.listChildMenu.length > 0) {
      flattenTree(node.listChildMenu, level + 1);
    }
  });
};

const initializeTree = () => {
  flatTreeData.value = [];
  flattenTree(props.treeData);
};

watch(() => props.treeData, initializeTree, { immediate: true });

const emit = defineEmits(['node-checked', 'node-click']);

const handleNodeClick = (node: TreeNode) => {
  node.expanded = !node.expanded;
  initializeTree();
  emit('node-click', node);
};

const handleCheckChange = (node: TreeNode) => {
  updateChildCheckStatus(node);
  updateParentCheckStatus(node);
  emit('node-checked', node);
};

const updateChildCheckStatus = (node: TreeNode) => {
  if (node.listChildMenu) {
    node.listChildMenu.forEach(child => {
      child.select = node.select;
      child.indeterminate = false;
      updateChildCheckStatus(child);
    });
  }
};

const updateParentCheckStatus = (node: TreeNode) => {
  const parentNode = findParentNode(node);
  if (parentNode) {
    const allChecked = parentNode.listChildMenu?.every(child => child.select) || false;
    const someChecked = parentNode.listChildMenu?.some(child => child.select || child.indeterminate) || false;

    parentNode.select = allChecked;
    parentNode.indeterminate = someChecked && !allChecked;

    updateParentCheckStatus(parentNode);
  }
};

const findParentNode = (node: TreeNode): TreeNode | undefined => {
  const findNodeById = (nodes: TreeNode[], id: number): TreeNode | undefined => {
    for (const n of nodes) {
      if (n.id === id) return n;
      if (n.listChildMenu) {
        const found = findNodeById(n.listChildMenu, id);
        if (found) return found;
      }
    }
    return undefined;
  };

  return findNodeById(props.treeData, node.parentId);
};

const selectAll = () => {
  if (props.allChecked) {
    selectNode(props.treeData);
  }
};

const selectNode = (nodes: TreeNode[]) => {
  nodes.forEach(node => {
    node.select = true;
    if (node.listChildMenu) {
      selectNode(node.listChildMenu);
    }
  });
};

const unSelectAll = () => {
  if (!props.allChecked) {
    unSelectNode(props.treeData);
  }
};

const unSelectNode = (nodes: TreeNode[]) => {
  nodes.forEach(node => {
    node.select = false;
    if (node.listChildMenu) {
      unSelectNode(node.listChildMenu);
    }
  });
};

watch(() => props.allChecked, () => {
  selectAll();
  unSelectAll();
});

const expandAll = () => {
  if (props.alleExpanded) {
    expandNode(props.treeData);
  }
};

const expandNode = (nodes: TreeNode[]) => {
  nodes.forEach(node => {
    node.expanded = true;
    if (node.listChildMenu) {
      expandNode(node.listChildMenu);
      initializeTree();
    }
  });
};

const collapseAll = () => {
  if (!props.alleExpanded) {
    collapseNode(props.treeData);
    initializeTree();
  }
};

const collapseNode = (nodes: TreeNode[]) => {
  nodes.forEach(node => {
    node.expanded = false;
    if (node.listChildMenu) {
      collapseNode(node.listChildMenu);
    }
  });
};

watch(() => props.alleExpanded, () => {
  expandAll();
  collapseAll();
});


// 根据子节点查找菜单节点
const findMenuNode = (treeData: TreeNode[], targetId: number): TreeNode | null => {
  const findNode = (nodes: TreeNode[], targetId: number, parent: TreeNode | null): TreeNode | null => {
    for (const node of nodes) {
      if (node.id === targetId) {
        return parent;
      }
      if (node.listChildMenu && node.listChildMenu.length > 0) {
        const result = findNode(node.listChildMenu, targetId, node);
        if (result) {
          return result;
        }
      }
    }
    return null;
  };

  return findNode(treeData, targetId, null);
};


//设置已选中的节点
// 设置已选中的节点
const setCheckedNodes = (checkedKeys: number[]) => {
  flatTreeData.value.forEach(node => {
    node.select = checkedKeys.includes(node.id);
  });

  // 更新子节点的选中状态
  flatTreeData.value.forEach(node => {
    if (node.listChildMenu) {
      updateChildCheckStatus(node);
    }
  });

  // 更新父节点的选中状态
  flatTreeData.value.forEach(node => {
    if (node.parentId !== null) {
      updateParentCheckStatus(node);
    }
  });
};

watch(() => props.checkedKeys, (newValue) => {
  setCheckedNodes(newValue);
}, { immediate: true });



defineExpose({
  selectAll,
  expandAll,
  unSelectAll,
  collapseAll,
});
</script>

<style scoped>
.custom-tree {
  width: 100%;
}

.tree-node {
  margin-left: 1rem;
}

.tree-node-label {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.tree-node-custom-content {
  /* 用于指定元素的宽度应该填充可用空间 */
  width: -webkit-fill-available;
  margin-left: 3.8rem;
}

.tree-node-label-text {
  margin-left: 10px;
}

.icon-caret-right {
  color: #acafb6;
  margin-right: 4px;
}

.icon-caret-bottom {
  color: #acafb6;
  transform: rotate(90deg);
  margin-right: 4px;
}

.tree-node-last {
  display: ruby;
}
</style>
